package com.solution.service;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

import org.apache.log4j.Logger;
import org.jboss.annotation.ejb.RemoteBinding;

import com.solution.constant.ErrorMessages;
import com.solution.constant.ExamErrorMessages;
import com.solution.constant.StatusConstants;
import com.solution.dao.AnswerTypeDAO;
import com.solution.dao.DAOFactory;
import com.solution.dao.ImageTypeDAO;
import com.solution.dao.QuestionTypeDAO;
import com.solution.exception.DAOException;
import com.solution.exception.ServiceBeanException;
import com.solution.model.AnswerType;
import com.solution.model.QuestionType;
import com.solution.util.SearchVO;

@Stateless
@Remote(QuestionTypeService.class)
@RemoteBinding(jndiBinding="QuestionTypeService")
@TransactionManagement(TransactionManagementType.CONTAINER)
public class QuestionTypeServiceImpl implements QuestionTypeService{

	@EJB
	private AnswerTypeService answerTypeService;
	
	/**
	 * DAO instance.
	 */
	private QuestionTypeDAO questionTypeDao = (QuestionTypeDAO)DAOFactory.getDAOInstance(QuestionTypeDAO.class.getName());
	private AnswerTypeDAO answerTypeDao = (AnswerTypeDAO)DAOFactory.getDAOInstance(AnswerTypeDAO.class.getName());
	private ImageTypeDAO imageTypeDao = (ImageTypeDAO)DAOFactory.getDAOInstance(ImageTypeDAO.class.getName());
	
	/**
	 * Logger instance.
	 */
	private Logger log = Logger.getLogger(QuestionTypeServiceImpl.class.getName());

	public QuestionType fetchQuestion(Long questionTypeId) throws ServiceBeanException{
		log.debug("Entering :: QuestionTypeServiceImpl:fetchQuestion()");
		try{
			if(questionTypeId != null)
				return questionTypeDao.read(questionTypeId);
			else
				throw new ServiceBeanException(ErrorMessages.MANDATORY_PARAMETER_MISSING_EXCEPTION+" questionTypeId");
		}
		catch(DAOException ex){
			log.error(ExamErrorMessages.QUESTION_READ_EXCEPTION, ex);
			throw new EJBException(ex);
		}
	}
	
	public Set<QuestionType> fetchActiveQuestions(Long categoryTypeId, Long levelTypeId, Long numberOfQs) throws ServiceBeanException{
		log.debug("Entering :: QuestionTypeServiceImpl:fetchQuestions()");
		try{
			if(categoryTypeId != null && levelTypeId != null && numberOfQs != null){
				SearchVO searchVO = new SearchVO();
				searchVO.addEqualsCondition("levelType.id", levelTypeId, true);
				searchVO.addEqualsCondition("categoryType.id", categoryTypeId, true);
				searchVO.addEqualsCondition("status", StatusConstants.ACTIVE);
				List<QuestionType> allPossibleQuestions = questionTypeDao.search(searchVO);
				
				if(allPossibleQuestions != null && allPossibleQuestions.size()>= numberOfQs){
					Set<QuestionType> uniqueQuestionSet = new HashSet<QuestionType>();
					if(allPossibleQuestions.size() > 1){
						addUniqueQuestionsToSet(uniqueQuestionSet, allPossibleQuestions, numberOfQs);
					}
					else if(allPossibleQuestions.size() == 1){
						uniqueQuestionSet.add(allPossibleQuestions.get(0));
					}
					
					// check the final list......
					if(uniqueQuestionSet.size() == numberOfQs){
						return uniqueQuestionSet;
					}
					else{
						throw new ServiceBeanException("Specified Number of Questions not available in the system");
					}
				}
				else{
					throw new ServiceBeanException("Specified Number of Questions not available in the system");
				}
			}
			else{
				throw new ServiceBeanException(ErrorMessages.MANDATORY_PARAMETER_MISSING_EXCEPTION+" categoryTypeId, levelTypeId, numberOfQs");
			}
		}
		catch(DAOException ex){
			log.error(ExamErrorMessages.QUESTION_READ_EXCEPTION, ex);
			throw new EJBException(ex);
		}
	}
	
	/**
	 * This method uniquely add elements to the set in a unique fashion.
	 * @param uniqueQuestionSet
	 * @param allPossibleQuestions
	 * @param numberOfQs
	 * @return
	 */
	private Set<QuestionType> addUniqueQuestionsToSet(Set<QuestionType> uniqueQuestionSet, 
		List<QuestionType> allPossibleQuestions, Long numberOfQs){
		int maxLoopSize = 0;
		while(uniqueQuestionSet.size() < numberOfQs && maxLoopSize < 10000){
			int uniqueBit = (int)(Math.random()*allPossibleQuestions.size());
			log.info("The unique bit is :"+uniqueBit);
			uniqueQuestionSet.add(allPossibleQuestions.get(uniqueBit));
			maxLoopSize++;
		}
		return uniqueQuestionSet;
	}
	
	public List<QuestionType> fetchQuestions(SearchVO searchVO) throws ServiceBeanException{
		log.debug("Entering :: QuestionTypeServiceImpl:fetchQuestions()");
		try{
			return questionTypeDao.search(searchVO);
		}
		catch(DAOException ex){
			log.error(ExamErrorMessages.QUESTION_READ_EXCEPTION, ex);
			throw new EJBException(ex);
		}
	}
	
	public QuestionType createQuestionAnswer(QuestionType question, List<AnswerType> answers) throws ServiceBeanException{
		log.debug("Entering :: QuestionTypeServiceImpl:createQuestionAnswer()");
		try{
			// Perform business validations.
			// check for the mandatory parameters.
			if(question.checkForMandatoryParameters() != null && question.checkForMandatoryParameters().size() > 0){
				String errorMsg = ErrorMessages.MANDATORY_PARAMETER_MISSING_EXCEPTION;
				Iterator<String> iter = question.checkForMandatoryParameters().iterator();
				while(iter.hasNext()){
					errorMsg = errorMsg+", "+iter.next();
				}
				throw new ServiceBeanException(errorMsg);
			}
			// Create the question in database.
			question = questionTypeDao.create(question);
			log.info("The new question created with ID : "+question.getId().longValue());
			
			// create the answers.
			for(AnswerType answer : answers){
				answer.setQuestionType(question);
				answerTypeService.createAnswer(answer);
			}
			log.debug("Exiting :: QuestionTypeServiceImpl:createQuestionAnswer()");
			return question;
		}
		catch(DAOException ex){
			log.error(ExamErrorMessages.QUESTION_CREATE_EXCEPTION, ex);
			throw new EJBException(ex);
		}
	}
	
	public QuestionType updateQuestionAnswer(QuestionType question, List<AnswerType> answers) throws ServiceBeanException{
		log.debug("Entering :: QuestionTypeServiceImpl:updateQuestionAnswer()");
		try{
			// Perform business validations.
			// check for the mandatory parameters.
			if(question.checkForMandatoryParameters() != null && question.checkForMandatoryParameters().size() > 0){
				String errorMsg = ErrorMessages.MANDATORY_PARAMETER_MISSING_EXCEPTION;
				Iterator<String> iter = question.checkForMandatoryParameters().iterator();
				while(iter.hasNext()){
					errorMsg = errorMsg+", "+iter.next();
				}
				throw new ServiceBeanException(errorMsg);
			}
			// Update the question in database.
			if(question.getImageType() != null && question.getImageType().getId() == null){
				QuestionType dbQuestion = questionTypeDao.read(question.getId());
				if(dbQuestion.getImageType() != null){
					question.getImageType().setId(dbQuestion.getImageType().getId());
				}
				else{
					question.setImageType(imageTypeDao.create(question.getImageType()));
				}
			}
			question = questionTypeDao.update(question);
			log.info("The new question updated with ID : "+question.getId().longValue());
			
			List<AnswerType> toBeDeletedAns = answerTypeService.fetchActiveAnswers(question.getId());
			
			// delete the old answers
			if(answers.size()>0){
				for(AnswerType answer : toBeDeletedAns){
					answerTypeDao.delete(answer.getId());
				}
			}
			
			// create the answers.
			for(AnswerType answer : answers){
				answer.setQuestionType(question);
				answerTypeService.createAnswer(answer);
			}
			log.debug("Exiting :: QuestionTypeServiceImpl:updateQuestionAnswer()");
			return question;
		}
		catch(DAOException ex){
			log.error(ExamErrorMessages.QUESTION_UPDATE_EXCEPTION, ex);
			throw new EJBException(ex);
		}
	}
}
